宏任务与微任务、Event Loop事件循环
chenpeng 2020-11-30 JS引擎
JS 语言的一大特点就是单线程,所有任务都在主线程上运行,,也就是说,同一个时间只能做一件事。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,就出现了 Event Loop。
# 1.Event Loop执行任务流程
- 同步和异步任务分别进入不同的执行场所,同步任务进入主线程,异步任务进去 Event Table 并注册函数
- 注册回调函数后,Event Table 会将任务移入 Event Queue
- 主线程内的同步任务执行完毕后,会去 Event Queue 读取异步任务,进入主线程执行
- 上述过程会不断重复,也就是 Event Loop
# 2.宏任务与微任务
宏任务:
- script(整体代码)
- setTimeout
- setInterval
- I/O
- requestAnimationFrame(浏览器环境)
- postMessage
- MessageChannel
- setImmediate(Nodejs 环境)
微任务:
- Promise.then
- queueMicrotask
- Object.observe(已废弃)
- MutationObserver(html5新特性)
- process.nextTick(Nodejs 环境)
# 3.宏任务与微任务的执行过程
- 执行一个宏任务(执行栈中没有就从事件队列中获取)(同步任务也属于宏任务)
- 执行过程中如果遇到微任务,就将它添加到微任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后 GUI 线程接管渲染
- 渲染完毕后,JS 线程继续接管,开始下一个宏任务(从事件队列中获取)
# 4.Node.js 中的事件循环
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- timers: 计时器阶段,用于处理
setTimeout以及setInterval的回调函数 - pending callbacks: 用于执行某些系统操作的回调,例如TCP错误
- idle, prepare: Node内部使用,不用做过多的了解
- poll: 轮询阶段,执行队列中的 I/O 队列,并检查定时器是否到时
- check: 执行
setImmediate的回调 - close callbacks: 处理关闭的回调,例如 socket.destroy()
process.nextTick 是Node.js中一个特殊的微任务,因此会为它单独提供一个队列,称为 next tick queue,并且其优先级大于其它的微任务,即若同时存在 process.nextTick 和 promise,则会先执行前者